import database.*
import org.apache.iotdb.isession.template.Template
import org.apache.iotdb.session.template.MeasurementNode
import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType
import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType
import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import java.util.Random


class IotDbTest {
    private val curTs = System.currentTimeMillis()
    private val db = DataMaster.instance(IotDbProperty("iotdb","本地 iotdb 数据库", "127.0.0.1", 6667, "root", "root")) as IotDbFactory

    private fun connection() {
        Assertions.assertTrue(db.connect())
    }

    @Test fun createDataBase() {
        Assertions.assertTrue(db.createDataBase("root.test"))
    }

    @Test fun deleteDataBase() {
        Assertions.assertTrue(db.deleteDataBase("root.test"))
    }

    @Test fun clearAll() {
        val databases = db.queryDataBases()
        databases!!.forEach {
            db.deleteDataBase(it)
        }
        val templateList = db.queryTemplates()
        templateList?.forEach { template ->
            val pathSet = db.queryPathsOfTemplate(template)
            pathSet?.forEach { db.unSetTemplate(it, template) }
            db.dropTemplate(template)
        }
    }

    @Test fun createTimeSeries() {
        val path = "root.test.timeseries1"
        val dataType = TSDataType.FLOAT
        val encoding = TSEncoding.RLE
        val compressor = CompressionType.SNAPPY
        Assertions.assertTrue(db.createTimeSeries(path, dataType, encoding, compressor))
    }

    @Test fun createMultiTimeSeries() {
        val paths = mutableListOf<String>()
        val types = mutableListOf<TSDataType>()
        val encodings = mutableListOf<TSEncoding>()
        val compressors = mutableListOf<CompressionType>()
        for (i in 0..2){
            paths.add("root.test.multiseries$i")
            types.add(TSDataType.FLOAT)
            encodings.add(TSEncoding.RLE)
            compressors.add(CompressionType.SNAPPY)
        }
        Assertions.assertTrue(db.createMultiTimeSeries(paths, types, encodings, compressors))
    }

    @Test fun createAlignedTimeSeries() {
        val path = "root.test.alignedseries"
        val measurements = mutableListOf<String>()
        val types = mutableListOf<TSDataType>()
        val encodings = mutableListOf<TSEncoding>()
        val compressors = mutableListOf<CompressionType>()
        for (i in 0..2){
            measurements.add("UUU$i")
            types.add(TSDataType.FLOAT)
            encodings.add(TSEncoding.RLE)
            compressors.add(CompressionType.SNAPPY)
        }
        Assertions.assertTrue(db.createAlignedTimeSeries(path, measurements, types, encodings, compressors))
    }

    @Test fun checkTimeSeriesExist() {
        val truePath = "root.test.alignedseries.UUU2"
        val falsePath = "root.test.alignedseries"
        Assertions.assertTrue(db.checkTimeSeries(truePath))
        Assertions.assertFalse(db.checkTimeSeries(falsePath))
    }

    @Test fun deleteTimeSeries() {
        val path = "root.test.Sck_20.Sck_20_1111.Energy"
        Assertions.assertTrue(db.deleteTimeSeries(path))
    }

    @Test fun deleteMultiTimeSeries() {
        val paths = mutableListOf<String>()
        for (i in 0..2){
            paths.add("root.test.multiseries$i")
        }
        Assertions.assertTrue(db.deleteMultiTimeSeries(paths))
    }

    @Test fun insertRecord() {
        val path = "root.test.alignedseries"
//        val measurements = mutableListOf("P", "U", "I", "timeseries1")
        val measurements = mutableListOf<String>()
        val types = mutableListOf<TSDataType>()
        val valueList = mutableListOf<Any>()
        for (i in 0..2) {
            measurements.add("UUU$i")
            types.add(TSDataType.FLOAT)
            valueList.add(Random().nextFloat())
        }
        types.forEach { println(it) }
        valueList.forEach { println(it) }

//        db.insertRecord("root.test", curTs, measurements, types, valueList, false)
        db.insertRecord(path, curTs, measurements, types, valueList, true)
    }

    @Test fun insertMultiRecords() {
        // 暂时用不到，未测试
    }

    // 注意，查询root.test.timeseries1的数据要到 root.test下查询，这里root.test是设备, timeseries1是measurement
    @Test fun queryRawSql() {
        val result = db.queryRawSql("select * from root.test")
        result!!.print()
    }

    @Test fun deleteRecord() {
        Assertions.assertTrue(db.deleteRecord("root.test.timeseries1", 1698118627614))
    }

    @Test fun deleteMultiRecord() {
        connection()
        // 暂时用不到，未测试
    }

    @Test fun queryTimeRange() {
        // 查询的时候，要到measurement这一级
        db.queryTimeRange(mutableListOf("root.D86570000029.Trm_dt.Trm_virtual3_00032.P"), 1630966400000, 1698163200000)!!.print()
    }

    @Test fun queryTimeSeries() {
        db.queryTimeSeries("root.**")
    }

    @Test fun queryLastRecord() {
        db.queryLastRecord(mutableListOf("root.test.P"), 1698076800000)
    }

    @Test fun showDataBases() {
        db.showDataBases()
    }

    @Test fun showTimeSeries() {
        db.showTimeSeries("root.db.**")
    }

    @Test fun countDataBases() {
        Assertions.assertEquals(1, db.countDataBases())
    }

    @Test fun countTimeSeries() {
        Assertions.assertEquals(7, db.countTimeSeries("root.test.**"))
    }

    @Test fun createTemplate() {
        val nameList = mutableListOf("U", "I", "P", "Energy")
        val name = "Socket"
        val measurementList = nameList.map { n ->
            MeasurementNode(n, TSDataType.FLOAT, TSEncoding.RLE, CompressionType.SNAPPY)
        }
        db.createTemplate(name, measurementList)
    }

    @Test fun setTemplate() {
        val templateName = "Socket"
        val prefixPath = "root.test.Sck_20"
        db.commandRawSql("set schema template $templateName to $prefixPath")
//        db.setTemplate(templateName, prefixPath)
    }

    @Test fun unSetTemplate() {
        val templateName = "Socket"
        val prefixPath = "root.test.template"
        db.unSetTemplate(templateName, prefixPath)
    }

    @Test fun showTemplates() {
        println(db.queryTemplates())
    }

    @Test fun dropTemplate() {
        val templateName = "t1"
        db.dropTemplate(templateName)
    }

    @Test fun resetDataBase() {
        val templateList = db.queryTemplates()
        templateList?.forEach { template ->
            val pathSet = db.queryPathsOfTemplate(template)
            pathSet?.forEach { db.unSetTemplate(it, template) }
            db.dropTemplate(template)
        }
        val seriesList = db.queryTimeSeries("root.**")
        if(seriesList != null) db.deleteMultiTimeSeries(seriesList)
        db.deleteDataBase("root.db")

    }

    @Test fun createTimeSeriesUsingTemplate() {
        // ** 似乎无需创建timeseries
        // 阅读和尝试 https://iotdb.apache.org/zh/UserGuide/V1.2.x/User-Manual/Operate-Metadata.html#%E6%8C%82%E8%BD%BD%E5%85%83%E6%95%B0%E6%8D%AE%E6%A8%A1%E6%9D%BF

        // "CREATE DATABASE root.test"
        db.createDataBase("root.test")
        println("--------------------------------------")
        // db.commandRawSql("create schema template t1 (temperature FLOAT encoding=RLE, status BOOLEAN encoding=PLAIN compression=SNAPPY)")
        // 创建元模板
        val templateName = "socket"
        val nameList = mutableListOf("U", "I", "P", "Energy")
        val measurementList = nameList.map { n ->
            MeasurementNode(n, TSDataType.FLOAT, TSEncoding.RLE, CompressionType.SNAPPY)
        }
        db.createTemplate(templateName, measurementList)
        println("--------------------------------------")
        // "set schema template t1 to root.db.sck_20"
        // 设置模板到时间序列
        db.setTemplate("socket", "root.test.sck_20")
        println("--------------------------------------")
        // 使用模板创建时间序列
        db.commandRawSql("create timeseries using schema template on root.test.sck_20.sck_20_1111")
        println("--------------------------------------")
        val path = "root.test.sck_20.sck_20_1111"
        val measurements = mutableListOf("U", "I", "P", "Energy")
        val types = mutableListOf<TSDataType>()
        val valueList = mutableListOf<Any>()
        for (i in 0..5) {
            types.add(TSDataType.FLOAT)
            valueList.add(Random().nextFloat())
        }
        db.insertRecord(path, curTs, measurements, types, valueList)
        println("--------------------------------------")
        // 显示结果
        db.commandRawSql("show timeseries root.db.**")
        println("--------------------------------------")
    }

    @Test fun showDataBaseDetail() {
        println("-------  show database  --------------")
        println(db.queryDataBases())
        println("-------  show templates  --------------")
        println(db.queryTemplates())
        println("-------  show timeseries  --------------")
        val ts = db.queryTimeSeries("root.**")
        println(ts)
        println("-------  show records  --------------")
        ts?.forEach { db.queryRecord(it)?.print() }
    }

    @Test fun officialCase() {
        db.commandRawSql("CREATE DATABASE root.db")
        db.commandRawSql("create schema template t1 (temperature FLOAT encoding=RLE, status BOOLEAN encoding=PLAIN compression=SNAPPY)")
        db.commandRawSql("set schema template t1 to root.db.sck_20")
        db.commandRawSql("create timeseries using schema template on root.db.sck_20")
        db.commandRawSql("show timeseries root.db.**")
    }
}
